package copyengine.ui.display.s9 {
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    import starling.core.RenderSupport;
    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.QuadBatch;
    import starling.display.Sprite;
    import starling.textures.Texture;
    import starling.textures.TextureSmoothing;
    import starling.utils.MatrixUtil;

    /**
     * Scales an image with nine regions to maintain the aspect ratio of the
     * corners regions. The top and bottom regions stretch horizontally, and the
     * left and right regions scale vertically. The center region stretches in
     * both directions to fill the remaining space.
     */
    public class Scale9Image extends Sprite {

        private static const HELPER_MATRIX:Matrix = new Matrix();
        private static const HELPER_POINT:Point = new Point();
        private static var helperImage:Image;


        private var mIsNeedRender:Boolean;
        private var mFrame:Rectangle;
        private var mTextures:Scale9Textures;
        private var mWidth:Number = NaN;
        private var mHeight:Number = NaN;
        private var mHitArea:Rectangle;
        private var mBatch:QuadBatch;

        //=======================
        //==Feathers 中为需要外部设置的参数
        //==此处直接使用默认值(不提供get/set)
        //======================
        private var mTextureScale:Number = 1;
        private var mSmoothing:String = TextureSmoothing.BILINEAR;
        private var mColor:uint = 0xffffff;

        public function Scale9Image(textures:Scale9Textures) {
            super();
            this.textures = textures;
            this.mHitArea = new Rectangle();
            this.readjustSize();

            this.mBatch = new QuadBatch();
            this.mBatch.touchable = false;
            this.addChild(this.mBatch);
        }

        public override function getBounds(targetSpace:DisplayObject, resultRect:Rectangle = null):Rectangle {
            if (!resultRect) {
                resultRect = new Rectangle();
            }

            var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE;
            var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;

            if (targetSpace == this) // optimization
            {
                minX = this.mHitArea.x;
                minY = this.mHitArea.y;
                maxX = this.mHitArea.x + this.mHitArea.width;
                maxY = this.mHitArea.y + this.mHitArea.height;
            }
            else {
                this.getTransformationMatrix(targetSpace, HELPER_MATRIX);

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x, this.mHitArea.y, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x, this.mHitArea.y + this.mHitArea.height, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x + this.mHitArea.width, this.mHitArea.y, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x + this.mHitArea.width, this.mHitArea.y + this.mHitArea.height, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;
            }

            resultRect.x = minX;
            resultRect.y = minY;
            resultRect.width = maxX - minX;
            resultRect.height = maxY - minY;

            return resultRect;
        }

        override public function hitTest(localPoint:Point, forTouch:Boolean = false):DisplayObject {
            if (forTouch && (!this.visible || !this.touchable)) {
                return null;
            }
            return this.mHitArea.containsPoint(localPoint) ? this : null;
        }

        override public function render(support:RenderSupport, parentAlpha:Number):void {
            this.doRender();
            super.render(support, parentAlpha);
        }

        public function doRender():void {
            if (!mIsNeedRender) {return;}

            mIsNeedRender = false;
            this.mBatch.reset();

            if (!helperImage) {
                //because Scale9Textures enforces infoTree, we know for sure that
                //this texture will have a size greater than zero, so there
                //won't be an error from Quad.
                helperImage = new Image(this.mTextures.middleCenter);
            }
            helperImage.smoothing = this.mSmoothing;
            helperImage.color = this.mColor;

            var grid:Rectangle = this.mTextures.scale9Grid;
            var scaledLeftWidth:Number = grid.x * this.mTextureScale;
            var scaledRightWidth:Number = (this.mFrame.width - grid.x - grid.width) * this.mTextureScale;
            var sumLeftAndRight:Number = scaledLeftWidth + scaledRightWidth;
            if (sumLeftAndRight > this.mWidth) {
                var distortionScale:Number = (this.mWidth / sumLeftAndRight);
                scaledLeftWidth *= distortionScale;
                scaledRightWidth *= distortionScale;
                sumLeftAndRight = scaledLeftWidth + scaledRightWidth;
            }
            var scaledCenterWidth:Number = this.mWidth - sumLeftAndRight;
            var scaledTopHeight:Number = grid.y * this.mTextureScale;
            var scaledBottomHeight:Number = (this.mFrame.height - grid.y - grid.height) * this.mTextureScale;
            var sumTopAndBottom:Number = scaledTopHeight + scaledBottomHeight;
            if (sumTopAndBottom > this.mHeight) {
                distortionScale = (this.mHeight / sumTopAndBottom);
                scaledTopHeight *= distortionScale;
                scaledBottomHeight *= distortionScale;
                sumTopAndBottom = scaledTopHeight + scaledBottomHeight;
            }
            var scaledMiddleHeight:Number = this.mHeight - sumTopAndBottom;

            if (scaledTopHeight > 0) {
                if (scaledLeftWidth > 0) {
                    helperImage.texture = this.mTextures.topLeft;
                    helperImage.readjustSize();
                    helperImage.width = scaledLeftWidth;
                    helperImage.height = scaledTopHeight;
                    helperImage.x = scaledLeftWidth - helperImage.width;
                    helperImage.y = scaledTopHeight - helperImage.height;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledCenterWidth > 0) {
                    helperImage.texture = this.mTextures.topCenter;
                    helperImage.readjustSize();
                    helperImage.width = scaledCenterWidth;
                    helperImage.height = scaledTopHeight;
                    helperImage.x = scaledLeftWidth;
                    helperImage.y = scaledTopHeight - helperImage.height;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledRightWidth > 0) {
                    helperImage.texture = this.mTextures.topRight;
                    helperImage.readjustSize();
                    helperImage.width = scaledRightWidth;
                    helperImage.height = scaledTopHeight;
                    helperImage.x = this.mWidth - scaledRightWidth;
                    helperImage.y = scaledTopHeight - helperImage.height;
                    this.mBatch.addImage(helperImage);
                }
            }

            if (scaledMiddleHeight > 0) {
                if (scaledLeftWidth > 0) {
                    helperImage.texture = this.mTextures.middleLeft;
                    helperImage.readjustSize();
                    helperImage.width = scaledLeftWidth;
                    helperImage.height = scaledMiddleHeight;
                    helperImage.x = scaledLeftWidth - helperImage.width;
                    helperImage.y = scaledTopHeight;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledCenterWidth > 0) {
                    helperImage.texture = this.mTextures.middleCenter;
                    helperImage.readjustSize();
                    helperImage.width = scaledCenterWidth;
                    helperImage.height = scaledMiddleHeight;
                    helperImage.x = scaledLeftWidth;
                    helperImage.y = scaledTopHeight;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledRightWidth > 0) {
                    helperImage.texture = this.mTextures.middleRight;
                    helperImage.readjustSize();
                    helperImage.width = scaledRightWidth;
                    helperImage.height = scaledMiddleHeight;
                    helperImage.x = this.mWidth - scaledRightWidth;
                    helperImage.y = scaledTopHeight;
                    this.mBatch.addImage(helperImage);
                }
            }

            if (scaledBottomHeight > 0) {
                if (scaledLeftWidth > 0) {
                    helperImage.texture = this.mTextures.bottomLeft;
                    helperImage.readjustSize();
                    helperImage.width = scaledLeftWidth;
                    helperImage.height = scaledBottomHeight;
                    helperImage.x = scaledLeftWidth - helperImage.width;
                    helperImage.y = this.mHeight - scaledBottomHeight;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledCenterWidth > 0) {
                    helperImage.texture = this.mTextures.bottomCenter;
                    helperImage.readjustSize();
                    helperImage.width = scaledCenterWidth;
                    helperImage.height = scaledBottomHeight;
                    helperImage.x = scaledLeftWidth;
                    helperImage.y = this.mHeight - scaledBottomHeight;
                    this.mBatch.addImage(helperImage);
                }

                if (scaledRightWidth > 0) {
                    helperImage.texture = this.mTextures.bottomRight;
                    helperImage.readjustSize();
                    helperImage.width = scaledRightWidth;
                    helperImage.height = scaledBottomHeight;
                    helperImage.x = this.mWidth - scaledRightWidth;
                    helperImage.y = this.mHeight - scaledBottomHeight;
                    this.mBatch.addImage(helperImage);
                }
            }
        }

        /**
         * Readjusts the dimensions of the image according to its current
         * textures. Call this method to synchronize image and texture size
         * after assigning textures with a different size.
         */
        public function readjustSize():void {
            this.width = this.mFrame.width * this.mTextureScale;
            this.height = this.mFrame.height * this.mTextureScale;
            mIsNeedRender = true;
        }

        public function get textures():Scale9Textures {return this.mTextures;}

        public function set textures(value:Scale9Textures):void {
            if (this.mTextures == value) {
                return;
            }
            this.mTextures = value;
            var texture:Texture = this.mTextures.texture;
            this.mFrame = texture.frame;
            if (!this.mFrame) {
                this.mFrame = new Rectangle(0, 0, texture.width, texture.height);
            }
            mIsNeedRender = true;
        }

        override public function get width():Number {return this.mWidth;}

        override public function set width(value:Number):void {
            if (this.mWidth == value) {
                return;
            }
            this.mWidth = this.mHitArea.width = value;
            mIsNeedRender = true;
        }

        override public function get height():Number {return this.mHeight;}

        override public function set height(value:Number):void {
            if (this.mHeight == value) {
                return;
            }
            this.mHeight = this.mHitArea.height = value;
            mIsNeedRender = true;
        }

    }
}